package utils;

import logs.Logger;

/**
 * Trida urcena pro sledovani pametove a casove narocnosti programu.
 * 
 * @author Petr Altman
 *
 */
public class PerformaceProbe implements Runnable {
	public static final int	DISABLED = 0, TIME = 1, MEMORY = 2;
	
	private static int type = DISABLED;
	
	/**
	 * Vrati nastaveny typ sledovani.
	 * @return Typ sledovani.
	 */
	public static int getType() {
		return type;
	}

	/**
	 * Nastavy typ sledovani.
	 * @param type Novy typ sledovani.
	 */
	public static void setType(int type) {
		PerformaceProbe.type = type;
	}

	private static boolean verbose = false;
	
	/**
	 * Vrati priznak, zda maji byt namerene udaje vypsany do konzole.
	 * @return Priznak vypsani udaju do konzole.
	 */
	public static boolean isVerbose() {
		return verbose;
	}

	/**
	 * Nastavi vypisovani namerenych udaju do konzole.
	 * @param verbose Priznak vypisovani udaju.
	 */
	public static void setVerbose(boolean verbose) {
		PerformaceProbe.verbose = verbose;
	}

	protected long startTime = 0, totalTime = 0;
	
	protected long startUsedMemory = 0, totalUsedMemory = 0;
	
	/**
	 * Vrati celkovy namereny cas.
	 * @return Celkovy namereny cas.
	 */
	public long getTotalTime() {
		return totalTime;
	}

	/**
	 * Vrati namerenou maximalni vyuzitou pamet.
	 * @return Namerena maximalni vyuzita pamet.
	 */
	public long getTotalUsedMemory() {
		return totalUsedMemory;
	}
	
	private Thread th = new Thread(this);
	
	private boolean isRunning = false;
	
	/**
	 * Cstor.
	 */
	public PerformaceProbe() {
		super();
	
	}
	
	/**
	 * Vrati aktualne pouzivane mnozstvi pameti.
	 * @return Aktualne pouzivane mnozstvi pameti.
	 */
	public long getUsedMemory() {
		Runtime.getRuntime().runFinalization();
		Runtime.getRuntime().gc();
		return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
	}
	
	/**
	 * Zahaji mereni.
	 */
	public void start() {
				
		switch(type) {
		case TIME:
			startUsedMemory = getUsedMemory();
			break;
		case MEMORY:
			totalUsedMemory = 0;			
			isRunning = true;
			th.start();
			break;
		}
		
		startTime = System.currentTimeMillis();
	}
	
	/**
	 * Ukonci mereni.
	 * @param print Priznak vypsani udaju do konzole.
	 */
	public void end(boolean print) {
		
		totalTime = System.currentTimeMillis() - startTime;
		
		switch(type) {
		case TIME:
			totalUsedMemory = getUsedMemory() - startUsedMemory;
			break;	
		case MEMORY:
		
			try {
				isRunning = false;
				th.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			break;
		}
		if(print)
			print();
	}
	
	/**
	 * Vypise do konzole namerene hodnoty.
	 */
	public void print() {
		Logger.addDebug("PP result: t = " + totalTime + " ms; m = " + (totalUsedMemory / 1024) + " KB", true);
	}

	@Override
	public void run() {
		while(isRunning) {
			long mem = getUsedMemory();
			if(mem > totalUsedMemory)
				totalUsedMemory = mem;
			
			if(verbose)
				Logger.addInfo("Used memory: " + (mem / 1024) + " KB", true);
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
		}
	}
}
